feat: nearby-watch radius in the configured display unit - #413
Merged
Conversation
First part of #10: a distance_unit plugin setting (m, km, ft, mi, nm) that drives what users see. Meters remain the internal unit everywhere (storage, SQL, filter wire format, REST API). - RedmineGtt::DistanceUnit: unit table, current/api_unit readers with fallback, to/from-meters conversion - distance column: header carries the unit (caption proc), list and CSV cells render converted via a QueriesHelper prepend - api_distance_unit setting reserved with meters as the only option, so the API contract can widen later without a settings migration - settings UI selects + en/ja/de locales The distance filter input and the nearby-watch radius field follow in separate PRs.
Second part of #10. The distance filter row displays and accepts its bounds in the configured unit: values coming from the server (meters) convert for display, and a submit hook on #query_form converts back to integer meters, so the wire format, saved queries and the API stay metric. The unit label renders next to the inputs. The hook binds via jQuery when present because core submits the form with $('#query_form').submit(), which bypasses native listeners.
Final part of #10, and the reason to land units before #14 ships: the watch radius is now stored in meters (the internal unit) and entered and displayed in the configured display unit on My account. - gtt_watch_radius stores meters; the account form goes through the gtt_watch_radius_in_unit virtual attribute (unit -> meters on write, meters -> unit on read); the raw key is no longer mass-assignable - NEARBY_WATCH_MAX_RADIUS_KM becomes NEARBY_WATCH_MAX_RADIUS_M (1,000,000 m); NearbyWatchers compares meters directly - the field's unit label and max derive from the setting; step allows fractions (0.5 km = 500 m) Since #14 is unreleased, no stored-preference migration is needed.
- unknown units fall back to meters instead of raising KeyError (the setting can be hand-edited, callers may pass user-supplied strings) - the CSV separator test derives its expectation from the locale instead of hardcoding '.'
There was a problem hiding this comment.
Pull request overview
Completes the multi-unit distance work for #10 by introducing a shared meters↔display-unit conversion layer and applying it to the distance column, distance filter UI, and the nearby-watch radius preference (now stored in meters while displayed/edited in the configured unit).
Changes:
- Add
RedmineGtt::DistanceUnit(and matching frontend unit table) plus new plugin settingsdistance_unitand reservedapi_distance_unit. - Convert distance column header/rendering and distance filter input to use the configured display unit while keeping wire/storage/API semantics in meters.
- Change nearby-watch radius storage to meters with a virtual form attribute
gtt_watch_radius_in_unit, updating UI + watcher engine + tests.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/user_preference_patch_test.rb | Updates/extends tests for meters-based nearby-watch radius + unit-facing virtual attribute. |
| test/unit/queries_helper_patch_test.rb | Adds coverage for distance column/list + CSV rendering in configured units. |
| test/unit/nearby_watchers_test.rb | Adjusts watcher tests to use meters radius values. |
| test/unit/distance_unit_test.rb | Adds unit tests for RedmineGtt::DistanceUnit defaults, clamping, and conversions. |
| test/functional/my_account_watch_nearby_test.rb | Updates account controller tests for the new form key gtt_watch_radius_in_unit and meters storage. |
| src/components/gtt-client/redmine/filters.ts | Implements display-unit rendering + submit-time conversion for distance filter row. |
| src/components/gtt-client/redmine/filters.test.ts | Adds frontend unit-table parity + conversion/noise-trimming tests. |
| lib/redmine_gtt/patches/user_preference_patch.rb | Stores radius in meters and adds gtt_watch_radius_in_unit virtual attribute; updates safe attributes. |
| lib/redmine_gtt/patches/queries_helper_patch.rb | Renders distance column/list + CSV values in configured display unit. |
| lib/redmine_gtt/patches/issue_query_patch.rb | Updates distance column header caption to include configured unit. |
| lib/redmine_gtt/nearby_watchers.rb | Compares distances directly in meters; uses meters max-radius constant. |
| lib/redmine_gtt/distance_unit.rb | Introduces server-side conversion helpers + supported unit tables. |
| lib/redmine_gtt.rb | Applies the new QueriesHelper patch during setup. |
| init.rb | Adds default plugin settings for distance_unit and api_distance_unit. |
| config/locales/ja.yml | Adds i18n strings for distance-unit settings and removes hardcoded watch-radius “km” label. |
| config/locales/en.yml | Adds i18n strings for distance-unit settings and removes hardcoded watch-radius “km” label. |
| config/locales/de.yml | Adds i18n strings for distance-unit settings and removes hardcoded watch-radius “km” label. |
| app/views/settings/gtt/_general.html.erb | Adds settings UI for selecting display unit and (reserved) API unit. |
| app/views/redmine_gtt/hooks/_watch_nearby_preference.html.erb | Updates watch-radius field to use unit-facing virtual attribute and display the configured unit. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- gtt_watch_radius_in_unit keeps 6 decimals for fractions (enough for 1 m in any supported unit), so a read-save round trip cannot drift the stored meters; regression test with a non-round mile value - the form's min is 1 meter expressed in the display unit instead of 0, which the server would treat as unset
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 3 of 3 for #10 (this PR completes it — the release PR can carry
Closes #10), stacked on #411/#412.This is the piece that motivated doing units before the 7.1.0 release: the #14 watch radius was stored in kilometers with a hardcoded "km" label. It now:
gtt_watch_radius_in_unitvirtual attribute the account form posts (unit → meters on write, meters → unit on read, garbage still rejected, blank clears); the raw meters key is no longer mass-assignablemax(1,000,000 m expressed in the unit) and fractional steps (0.5 km = 500 m) all derive from the settingNEARBY_WATCH_MAX_RADIUS_Mreplaces the km constant; the watcher engine compares meters directlySince #14 has never been released, there is no stored-preference migration — the semantics change while the feature is still only on
next.Verified: full plugin suite in the Redmine 7.0 mirror (101 runs, 539 assertions, 0 failures) and live in a browser with
distance_unit=km: stored 25000 m renders as25 kmwith max 1000; saving30stores"30000"meters and reads back as 30.